﻿using System;
using System.IO;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using ComponentFactory.Krypton.Toolkit;
using Xeno.Core;
using Xeno.Module;
using Polenter.Serialization;

using OpenTK;
using OpenTK.Graphics.OpenGL;
using OpenTK.Graphics;
namespace XenoStudio
{
    public partial class NardoVinchi : KryptonForm 
    {
        public static ShadeyNodeControl ActiveNode
        {
            get
            {
                return mActiveNode;
            }
            set
            {
                if (value == mActiveNode)
                {
                    return;
                }
                mActiveNode = value;
                Active.UpdateParArea();
            }
        }
        private static ShadeyNodeControl mActiveNode = null;
        public static NardoVinchi Active {get;set;}
        public XenoEffectNode MasterNode { get; set; }
        public Dictionary<object, object> ObjMap = new Dictionary<object, object>();
        public NardoVinchi()
        {
            InitializeComponent();
            ActiveNode = null;
           Active = this;
            CreateMaster();
            CreateMenu();
            InitConverters();
            
           
        }

        public object SetObj(object key, object obj)
        {
            if (ObjMap.ContainsKey(key))
            {
                ObjMap.Remove(key);
            }
            ObjMap.Add(key, obj);
            return obj;
        }
        public object GetObj(object key)
        {
            return ObjMap[key];
        }
        public object this[object key]
        {
            get
            {
                return GetObj(key);
            }
            set
            {
                SetObj(key, value);
            }
        }
        public void UpdateParArea()
        {
            ParArea.Controls.Clear();
            if (ActiveNode != null)
            {

                var t = ActiveNode.Node.GetType();

                int dx = 10;
                int dy = 10;

                foreach (var p in t.GetProperties())
                {
                    if (p.Name.Length < 5)
                    {
                        continue;
                    }
                    if (p.Name.Substring(0, 4) == "Prop")
                    {
                        var l = new KryptonLabel();
                        l.Left = dx;
                        l.Top = dy;
                        l.Width = 80;
                        l.Height = 30;
                        l.Text = p.Name.Substring(4);
                        ParArea.Controls.Add(l);
                        if (p.PropertyType == typeof(string))
                        {
                            var ta = new KryptonTextBox();
                            ta.Left = dx + 60;
                            ta.Top = dy;
                            ta.Width = 200;
                            ta.Height = 20;
                            SyncTextBox(p, ta);

                            ParArea.Controls.Add(ta);
                            //ObjMap.Add(ta, p);
                            //ObjMap.Add(p,
                            this[ta] = p;
                            this[p] = ActiveNode.Node;
                            ta.TextChanged += new EventHandler(ta_TextChanged);
                        }
                        if (p.PropertyType == typeof(float))
                        {


                            var ta = new KryptonTextBox();
                            ta.Left = dx + 60;
                            ta.Top = dy;
                            ta.Width = 200;
                            ta.Height = 20;
                            //ta.Text = p.GetValue(ActiveNode.Node, null).ToString();
                            SyncTextBox(p, ta);
                            ParArea.Controls.Add(ta);
                            this[ta] = p;
                            this[p] = ActiveNode.Node;
                            ta.TextChanged += new EventHandler(ta_TextChanged);
                        }

                        dy += 40;
                    }
                }

            }
        }

        private static void SyncTextBox(System.Reflection.PropertyInfo p, KryptonTextBox ta)
        {
            if (p != null && ActiveNode != null && ActiveNode.Node != null)
            {
                var val = p.GetValue(ActiveNode.Node, null);
                if (val != null)
                {
                    ta.Text = val.ToString();
                }
            }
        }
        private Dictionary<Type, ConvertObject> ConvFromText = new Dictionary<Type, ConvertObject>();

        public delegate object ConvertObject(object obj);

        public void InitConverters()
        {
            ConvFromText[typeof(float)] = new ConvertObject(TEXT_TO_FLOAT);
            ConvFromText[typeof(string)] = new ConvertObject(NO_CONVERT);
        }
        public object ConvertFromText(Type to,string text)
        {
            return ConvFromText[to](text as object);
        }
        public object NO_CONVERT(object obj)
        {
            return obj;
        }
        public object TEXT_TO_FLOAT(object f)
        {
            float fv = 0.0f;
            float.TryParse(f as string, out fv);
            return fv as object;
            //return float.Parse(f as string) as object;
        }
        public object FLOAT_TO_TEXT(object f)
        {
            return f.ToString() as object;
        }
        void ta_TextChanged(object sender, EventArgs e)
        {
            var ap = this[sender] as System.Reflection.PropertyInfo;
            var ac = sender as KryptonTextBox;
            var ao = this[ap];
            ap.SetValue(ao,ConvertFromText(ap.PropertyType,ac.Text),null);

           
        }
        private Dictionary<KryptonButton, XenoEffectNode> SelLUT = new Dictionary<KryptonButton, XenoEffectNode>();
        public void CreateMenu()
        {
            int dy = 15;
            foreach (var n in XenoFX.NodeTypes)
            {
                var b = new KryptonButton();
                b.Left = 15;
                b.Top = dy;
                b.Width = SelArea.ClientRectangle.Width - 30;
                b.Height = 30;
                b.Text = n.ID;
                SelArea.Controls.Add(b);
                b.PaletteMode = ComponentFactory.Krypton.Toolkit.PaletteMode.SparklePurple;
                dy += 50;
                b.Click += new EventHandler(b_Click);
                SelLUT.Add(b, n);
            }
        }

        void b_Click(object sender, EventArgs e)
        {
            var b = sender as KryptonButton;
            var n = SelLUT[b];
            var nn = n.CreateNew();
            nn.UIPos = new OpenTK.Vector2(150, 100);
            nn.UIWidth = 150;
            if (nn.In.Count > 0 && nn.Out.Count > 0)
            {
                nn.UIWidth *= 1.8f;
            }
            int h1, h2;
            h1 = nn.In.Count * 20;
            h2 = nn.Out.Count * 20;
            int bh = 0;
            if (h1 > h2)
            {
                bh = h1;
            }
            else
            {
                bh = h2;
            }
            nn.UIHeight = 30 + bh;
            EditArea.AddNode(nn);
        }
        public void CreateMaster()
        {
            var n = new XenoEffectNode("Master", EffectNodeType.EngineInput);
            n.AddIn(EffectParameterType.Anything, "Diffuse", "Diffuse Color Value");
            n.AddIn(EffectParameterType.Vector3, "Normal", "Normal Vector Value");
            n.AddIn(EffectParameterType.Anything, "Specular", "Specular Color Value");
            n.AddIn(EffectParameterType.Anything, "Gloss", "Gloss Value");
            n.AddIn(EffectParameterType.Anything, "Ambient", "Ambient Value");
            n.AddIn(EffectParameterType.Anything, "Glow", "Glow Value");
            n.UIPos = new OpenTK.Vector2(80, 80);
            n.UIWidth = 140;
            n.UIHeight = 240;
            EditArea.AddNode(n);
            MasterNode = n;
        }

        private void kryptonSplitContainer2_Panel2_Paint(object sender, PaintEventArgs e)
        {
                    }
        private XenoNode PreviewMesh = null;
        private XenoNode[] PreviewLight = new XenoNode[1024];
        private XenoMaterial PreviewMaterial = null;
        private XenoEffect PreviewEffect = null;
        private void NardoVinchi_Load(object sender, EventArgs e)
        {

            PreviewScene = new XenoGraph("PreviewScene001");
            PreviewCam = XenoFactory.Node.Camera("PreviewCamera001", PreviewScene.RootNode);
            XenoCore.CurrentView = PreviewCam;
            PreviewCam.LocalPosition = new Vector3(0, 0, 2);
        //    PreviewMesh = XenoFactory.Mesh.Pyramid(XenoFactory.Node.Mesh("PreviewMesh001",PreviewScene.RootNode), 1);
            PreviewMesh = XenoImport.ImportAI("Xeno/Prefab/PrefabPlane.dae", "PreviewMesh001", PreviewScene.RootNode, 1.0f);

            PreviewLight[0] = XenoFactory.Light.Directional("PreviewLight001", PreviewScene.RootNode);
            PreviewMaterial = new XenoMaterial("PreviewMaterial001");
            PreviewEffect = PreviewMaterial.Effect;

            List<XenoNode> matList = PreviewScene.RootNode.AllWithType(typeof(XenoMeshRenderer), null);

            foreach (var node in matList)
            {
                node.MeshRenderer.Material = PreviewMaterial;
            }

            PreviewMaterial.DiffuseMap = new XenoTexture2D("brick_Diffuse.jpg");
            //PreviewMesh.MeshRenderer.Material = PreviewMaterial;
            

        }


        private void kryptonButton1_Click(object sender, EventArgs e)
        {
            Op_NewGraph();
        }

        private void Op_NewGraph()
        {
            EditArea.Nodes.Clear();
            EditArea.NodeArea.NodeLUT.Clear();
            EditArea.NodeLUT.Clear();
            CreateMaster();
        }

        private void newGraphToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Op_NewGraph();
        }

        private void kryptonButton4_Click(object sender, EventArgs e)
        {
            if(ShaderPath.Text.Length<1){
                MessageBox.Show("Unable to create shader with no name.\nThis isn't a Clint Eastwood production.");
                return;
            }
            SaveGraphToDisk();
            MessageBox.Show("Succesfully outputted Shader.");
        }

        private void SaveGraphToDisk()
        {
            XenoFX.ConvertGraphToShader(MasterNode, ShaderPath.Text);
        }

        private void kryptonSplitContainer3_Panel2_Paint(object sender, PaintEventArgs e)
        {

        }
        private XenoNode PreviewCam = null;
        private XenoGraph PreviewScene = null; 
        private void glControl1_Paint(object sender, PaintEventArgs e)
        {
            glControl1.MakeCurrent();
            // Environment.Exit(-1);
            XenoCore.CurrentView = PreviewCam;
            XenoCore.DisplayWidth = glControl1.Width;
            XenoCore.DisplayHeight = glControl1.Height;
            GL.Viewport(0, 0, glControl1.Width, glControl1.Height);
       
            PreviewCam.Camera.Projection = Matrix4.CreatePerspectiveFieldOfView(MathHelper.DegreesToRadians(PreviewCam.Camera.FieldOfView), (float)glControl1.Width / (float)glControl1.Height, 0.1f, 1000.0f);

            
           GL.ClearColor(Color.Aqua);


            GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
            XenoCore.LightList = PreviewScene.RootNode.AllWithType(typeof(XenoMod_Light), null);
            PreviewScene.RenderGraph();


            glControl1.SwapBuffers();


        }

        private void glControl1_Resize(object sender, EventArgs e)
        {
            
            if (PreviewCam != null)
            {
         
           
            }
            glControl1.Invalidate();
        
          }

        private void glControl1_Load(object sender, EventArgs e)
        {

        }

        private void kryptonSplitContainer3_Panel2_Resize(object sender, EventArgs e)
        {
            
        }

        private void kryptonButton5_Click(object sender, EventArgs e)
        {
            SaveGraphToDisk();
            PreviewMaterial.Effect = new XenoEffect("Content/Shader/XenoMaterial_DirLight.vertex", ShaderPath.Text);
            glControl1.Invalidate();

        }

        private void toolStripButton1_Click(object sender, EventArgs e)
        {
            openFileDialog1.Title = "Select 3D Mesh to import.";
            openFileDialog1.ShowDialog();
            var path = openFileDialog1.FileName;
            if (System.IO.File.Exists(path))
            {
                PreviewScene.RootNode.Nodes.Remove(PreviewMesh);
                PreviewMesh = XenoImport.ImportAI(path, "PreviewMesh0001", PreviewScene.RootNode, 1.0f);
            }
            var bb = PreviewMesh.BoundingBox;
            PreviewCam.LocalPosition = new Vector3(0, bb[0].Y + (bb[1].Y - bb[0].Y) * 0.25f, bb[1].Z* 6.0f);
            PreviewCam.LookAt(new Vector3(bb[0].X + (bb[1].X - bb[0].X) / 2.0f, bb[0].Y + (bb[1].Y - bb[0].Y) / 2.0f, bb[0].Z + (bb[1].Z - bb[0].Z) / 2.0f));
            PreviewLight[0].LocalPosition = PreviewCam.LocalPosition;
            glControl1.Invalidate();


        }
        public void LoadGraphProjectFromDisk(string path)
        {
            EditArea.Nodes.Clear();
            XenoEffectNode.ParMap.Clear();
            XenoEffectNode.NodeMap.Clear();
          
            FileStream fs;
            BinaryReader r;
            CreateLoader(path, out fs,out r);

            //List<XenoEffectNode> nodes = s.Deserialize(fs) as List<XenoEffectNode>;

            int nc = r.ReadInt32();

            for (int i = 0; i < nc; i++)
            {
                var ep = XenoEffectNode.ReadParameter(r);
                XenoEffectNode.ParMap[ep.ThisName] = ep;
            }
            nc = r.ReadInt32();
            for (int i = 0; i < nc; i++)
            {
                var n = XenoEffectNode.ReadNode(r);
                if (n.PropNodeName == "Master")
                {
                    MasterNode = n;
                }
                XenoEffectNode.NodeMap[n.PropNodeName] = n;
            }
            foreach (var k in XenoEffectNode.ParMap.Keys)
            {
                var ep = XenoEffectNode.ParMap[k];
                ep.EstablishLinks();
            }
            if (r.ReadInt32() != 255)
            {
                MessageBox.Show("Although importing wastechnically succesfull, the actual end-code did not match\nConsult the developers on the forums.");
            }

    

            fs.Close();

            fs = null;

            foreach (var k in XenoEffectNode.NodeMap.Keys)
            {
                var n = XenoEffectNode.NodeMap[k];
                EditArea.AddNode(n);
            }

        }

        public void SetProjectNodes(List<XenoEffectNode> nodes)
        {
            EditArea.Nodes.Clear();
            foreach (var n in nodes)
            {
                EditArea.AddNode(n);
            }

        }


        public void SaveGraphProjectToDisk(string path)
        {
        
            FileStream fs;
            BinaryWriter w;
            CreateSaver(path,  out fs,out w);

            int nc = 0;
            foreach (var n in EditArea.Nodes)
            {

                nc += (n.In.Count + n.Out.Count);
            }
            w.Write(nc);
            foreach (var n in EditArea.Nodes)
            {
                n.WriteParameters(w);
            }
            w.Write(EditArea.Nodes.Count);

            foreach (var n in EditArea.Nodes)
            {
                n.WriteNode(w);
            }

            w.Write((int)255);
                                                 
            fs.Flush();

            fs.Close();

            fs = null;
            
        }
        private static void CreateLoader(string path,out FileStream fs,out BinaryReader br)
        {
         
            fs = new FileStream(path, FileMode.Open, FileAccess.Read);
            br = new BinaryReader(fs);
        }
        private static void CreateSaver(string path,  out FileStream fs,out BinaryWriter bw)
        {
          
            fs = new FileStream(path, FileMode.Create, FileAccess.Write);
            bw = new BinaryWriter(fs);

        }

        private static SharpSerializer CreateIO()
        {
            SharpSerializer s;
            SharpSerializerXmlSettings settings = new SharpSerializerXmlSettings();

            s = new SharpSerializer(BinarySerializationMode.SizeOptimized);
            return s;
        }
        private string LastSavePath = "None";
        private void kryptonButton2_Click(object sender, EventArgs e)
        {
           
            saveFileDialog1.Title = "Select .Graph File To Save Shader As...";
            saveFileDialog1.InitialDirectory = "Xeno/Graph/";
            saveFileDialog1.AddExtension = true;
            saveFileDialog1.DefaultExt = ".fxg";
            saveFileDialog1.CheckFileExists = false;
            saveFileDialog1.OverwritePrompt = true;
            saveFileDialog1.ShowDialog();

            var path = saveFileDialog1.FileName;

            if (path != null && path.Length > 0)
            {
                SaveGraphProjectToDisk(path);
                MessageBox.Show("Succesfully saved Project Graph to Disk!");
            }
            else
            {
                MessageBox.Show("Sorry, that is not a valid graph path. Please re-try!");
                return;
            }

        
        }

        private void kryptonButton3_Click(object sender, EventArgs e)
        {
            openFileDialog1.Title = "Select .fxg Graph file to Load...";
            openFileDialog1.AddExtension = true;
            openFileDialog1.DefaultExt = ".fxg";
            openFileDialog1.ShowDialog();

            var path = openFileDialog1.FileName;
            if (path != null && path.Length > 0 && File.Exists(path))
            {
                LoadGraphProjectFromDisk(path);
                MessageBox.Show("Project loaded!");
            }
            else
            {
                MessageBox.Show("Unable to load graph from specified path, please re-try!");
            }
            EditArea.Invalidate();
            EditArea.Dirty = true;
        }

    }
}
